var dynamicObjectCanvas = document.createElement('canvas');
var occlusionCanvas = document.createElement('canvas');

function drawFov(context, mapData, player) {
    context.save();
    var mapWidthPx = mapData.width * mapData.tileSize;
    var mapHeightPx = mapData.height * mapData.tileSize;
    if (occlusionCanvas.width !== mapWidthPx) {
        occlusionCanvas.width = mapWidthPx;
    }
    if (occlusionCanvas.height !== mapHeightPx) {
        occlusionCanvas.height = mapHeightPx;
    }
    if (dynamicObjectCanvas.width !== mapWidthPx) {
        dynamicObjectCanvas.width = mapWidthPx;
    }
    if (dynamicObjectCanvas.height !== mapHeightPx) {
        dynamicObjectCanvas.height = mapHeightPx;
    }
    var occlusionCtx = occlusionCanvas.getContext("2d");
    occlusionCtx.save();
    var dynamicObjectCtx = dynamicObjectCanvas.getContext("2d");
    dynamicObjectCtx.save();

    occlusionCtx.setTransform(1, 0, 0, 1, 0, 0);
    occlusionCtx.clearRect(0, 0, mapWidthPx, mapHeightPx);
    dynamicObjectCtx.setTransform(1, 0, 0, 1, 0, 0);
    dynamicObjectCtx.clearRect(0, 0, mapWidthPx, mapHeightPx);

    occlusionCtx.fillStyle = 'black';
    if (player) {
        drawPolygons(occlusionCtx, getOcclusionPolygons(mapData, player));
    }
    context.globalAlpha = 0.5;
    context.drawImage(occlusionCanvas, 0, 0);

    drawEffects(dynamicObjectCtx);
    drawEntities(dynamicObjectCtx);

    dynamicObjectCtx.globalCompositeOperation = 'destination-out';
    dynamicObjectCtx.drawImage(occlusionCanvas, 0, 0);
    context.globalAlpha = 1;
    context.drawImage(dynamicObjectCanvas, 0, 0);

    occlusionCtx.restore();
    dynamicObjectCtx.restore();
    context.restore();
}

function getMapDiagonalPx(mapData) {
    var widthPx = mapData.width * mapData.tileSize;
    var heightPx = mapData.height * mapData.tileSize;
    return Math.sqrt(widthPx * widthPx + heightPx * heightPx);
}

function getOcclusionPolygons(mapData, entity) {
    var occlusionPolygons = mapData.solidAreas.map(function(area) {
        var edges = getEdges(area, mapData, entity);
        var playerToEdgeLeft = edges.left.sub(entity.lerpPos);
        var playerToEdgeRight = edges.right.sub(entity.lerpPos);

        // Use diagonal of total map as upper bound for length of occlusions (to be optimized)
        var polyLength = getMapDiagonalPx(mapData);
        var edgeLeftExtended = playerToEdgeLeft.norm().mulS(polyLength).add(edges.left);
        var edgeRightExtended = playerToEdgeRight.norm().mulS(polyLength).add(edges.right);
        var occlusionPolygon = new Array();
        occlusionPolygon.push(edges.left);
        if (edges.middle) {
            occlusionPolygon.push(edges.middle);
        }
        occlusionPolygon.push(edges.right);
        occlusionPolygon.push(edgeRightExtended);
        occlusionPolygon.push(edgeLeftExtended);
        return occlusionPolygon;
    });
    return occlusionPolygons;
}

function getEdges(area, mapData, entity) {
    var leftX = area.topLeft.x * mapData.tileSize;
    var rightX = (area.bottomRight.x + 1) * mapData.tileSize;
    var topY = area.topLeft.y * mapData.tileSize;
    var bottomY = (area.bottomRight.y + 1) * mapData.tileSize;
    var edges = new Object();
    if (entity.lerpPos.x < leftX && entity.lerpPos.y < topY) {
        edges.left = new Vector(rightX, topY);
        edges.middle = new Vector(leftX, topY);
        edges.right = new Vector(leftX, bottomY);
    } else if (entity.lerpPos.x >= leftX && entity.lerpPos.x < rightX && entity.lerpPos.y < topY) {
        edges.left = new Vector(rightX, topY);
        edges.middle = null;
        edges.right = new Vector(leftX, topY);
    } else if (entity.lerpPos.x >= rightX && entity.lerpPos.y < topY) {
        edges.left = new Vector(rightX, bottomY);
        edges.middle = new Vector(rightX, topY);
        edges.right = new Vector(leftX, topY);
    } else if (entity.lerpPos.x >= rightX && entity.lerpPos.y >= topY && entity.lerpPos.y < bottomY) {
        edges.left = new Vector(rightX, bottomY);
        edges.middle = null;
        edges.right = new Vector(rightX, topY);
    } else if (entity.lerpPos.x >= rightX && entity.lerpPos.y >= bottomY) {
        edges.left = new Vector(leftX, bottomY);
        edges.middle = new Vector(rightX, bottomY);
        edges.right = new Vector(rightX, topY);
    } else if (entity.lerpPos.x >= leftX && entity.lerpPos.x < rightX && entity.lerpPos.y >= bottomY) {
        edges.left = new Vector(leftX, bottomY);
        edges.middle = null;
        edges.right = new Vector(rightX, bottomY);
    } else if (entity.lerpPos.x < leftX && entity.lerpPos.y >= bottomY) {
        edges.left = new Vector(leftX, topY);
        edges.middle = new Vector(leftX, bottomY);
        edges.right = new Vector(rightX, bottomY);
    } else if (entity.lerpPos.x < leftX && entity.lerpPos.y >= topY && entity.lerpPos.y < bottomY) {
        edges.left = new Vector(leftX, topY);
        edges.middle = null;
        edges.right = new Vector(leftX, bottomY);
    }
    return edges;
}

function drawPolygons(context, polygons) {
    for (i = 0; i < polygons.length; i++) {
        var polygon = polygons[i];
        context.beginPath();
        if (polygon.length > 0) {
            context.moveTo(polygon[0].x, polygon[0].y);
        }
        for (j = 1; j < polygon.length; j++) {
            context.lineTo(polygon[j].x, polygon[j].y);
        }
        context.fill();
    }
}
